package org.example.thread.futuretask.demo1;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

/**
 * Created by guo on 2018/2/15.
 * 演示可取消任务的股票交易处理程序
 * 创建可取消的任务。
 * 取消任务可以使用执行器返回的Future对象，而创建和执行任务可以使用前面讨论的FutureTask类。
 * 开发可以处理上百万次请求的模拟器。会发送数千条数据交易请求给模拟器。模拟器包含的线程池用于处理这些请求。
 * 还将编写一个“邪恶”的线程，它会随机选择诺干订单，并且尝试取消他们。如果订单已经执行，取消请求会失败。
 * 如果在订单在被分配给线程执行之前接收到取消请求，那么订单会被取消。如果交易订单正在执行。并且线程可被中断，
 * 那么在订单处理过程中接收的取消请求会结束剩余的处理流程。从而取消订单。
 *
 * 作者：Guo_1_9
 * 链接：https://juejin.cn/post/6844903565178191885
 * 来源：掘金
 * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
 */
public class StocksOrderProcessor {
    static final int MAX_NUMBER_OF_ORDER = 1_000_000;       //交易订单
    //1、创建数量为1000的线程池来执行订单。经过测试1000个线程，CPU维持在70%-80%左右。
    static private ExecutorService executor = Executors.newFixedThreadPool(1000);
    //2、创建ArrayList来保存执行执行订单的引用
    static private List<Future> ordersToProcess = new ArrayList<>();

    /**
     * 创建内部私有类OrderExecutor以处理订单执行的业务逻辑。
     * OrderExecutor实现了Callable接口以支持异步调用。
     */
    public static class OrderExecutor implements Callable {
        int id = 0;
        int count = 0;

        //3、传入整型变量id来记录订单编号。
        public OrderExecutor(int id) {
            this.id = id;
        }

        @Override
        public Object call() throws Exception {
            try {
                //4、将技术设为1000，每次计数前，让线程休眠一段不同的时间
                while (count < 1000) {
                    count++;
                    //5、通过让线程休眠一段不同的时间，模拟现实中每个订单需要不同的处理时间。
                    Thread.sleep(new Random(
                            System.currentTimeMillis() % 10).nextInt(10));
                }
                System.out.println("Successfully executed order:订单执行成功订单号为" + id);
            } catch (Exception ex) {
                throw (ex);
            }
            return id;
        }
    }

    public static void main(String[] args) {

        System.out.printf("Submitting %d trades订单交易开始%n", MAX_NUMBER_OF_ORDER);
        //6、通过循环遍历，提交一百万订单。
        for (int i = 0; i < MAX_NUMBER_OF_ORDER; i++) {
            submitOrder(i);
        }
        //7、创建“邪恶”线程尝试随机的取消某些订单。
        //每当执行到这里时，就会创建一些取消请求，并针对待处理的订单列表中存储的Future对象执行。
        new Thread(new EvilThread(ordersToProcess)).start();


        System.out.println("Cancelling a few order at random取消一些随机订单之后");
        try {

            //8a、某些订单可能已经被处理，模拟器就会继续处理剩余订单。
            // b、如果订单在执行器分配线程之前被取消，就将永远不会执行。
            // c、为了留有足够的时间结束所有待处理的订单，让执行器等待30秒。
            executor.awaitTermination(30, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        System.out.println("Checking status before shutdown在线程池关闭之前检查状态");
        int count = 0;
        //9a、通过循环遍历，统计有多少订单被成功取消。
        // b、对于订单中每一个Future对象，调用isCancelld方法。
        // c、如果对应的被成功取消，则方法返回true
        for (Future f : ordersToProcess) {
            if (f.isCancelled()) {
                count++;
            }
        }
        System.out.printf("%d trades cancelled订单被取消%n", count);
        //10、立即停止执行器释放分配的所有资源 （貌似我的百万订单根本停不下来啊，求解！）
        executor.shutdownNow();

    }

    private static void submitOrder(int id) {
        //6、a 创建一个Callable实例，每个实例都有为一个的Id供跟踪
        Callable<Integer> callable = new OrderExecutor(id);
        //6、b 调用ExecutorService的submit方法可将创建的任务提交以待执行。
        //并且将submit方法返回的对象放到待处理订单的数组里列表中。
        ordersToProcess.add(executor.submit(callable));
    }

}
